GCP metadata server から spreadsheet などの API 叩けるか
#GoogleCloud
叩ける
spreadsheet にアクセスできる scope が必要
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/spreadsheets.readonly
など
GCP 内
サービスアカウントキーなくてもアタッチされたサービスアカウントに共有すれば叩ける
ローカル(Application Default Credential)
gcloud auth application-default login 時に scope を指定する
$ gcloud auth application-default login --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/spreadsheets.readonly
---.icon
以下調査やコードリーディング
$ gcloud auth print-access-token
スコープ設定できない
Method: projects.serviceAccounts.generateAccessToken から作れないか
やってない
Matadata Server に scope 付けてリクエストしたら取れる説
Can GKE Workload Identity be used to access Google Sheets? - Stack Overflow
Workload Identity を有効にしたクラスタ & サービスアカウントを用意する
GSA のメールアドレスに spreadsheet を共有する
$ kubectl run -it --rm gcloud-slim --image=google/cloud-sdk:slim --restart=Never --serviceaccount={KSA} -- bash
gcloud じゃなくていいけど
$ curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A//www.googleapis.com/auth/spreadsheets.readonly
OAuth2 Access Token
$ curl -X GET -H 'Authorization: Bearer {ACCESS_TOKEN}' https://sheets.googleapis.com/v4/spreadsheets/{SPREADSHEET_ID}
プロジェクトの Spreadsheet API が有効でないといけない
https://console.developers.google.com/apis/api/sheets.googleapis.com/overview
とれる!!
scope を省略する(=cloud-platform) だと取れない
code:err
{
"error": {
"code": 403,
"message": "Request had insufficient authentication scopes.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT",
"domain": "googleapis.com",
"metadata": {
"service": "sheets.googleapis.com",
"method": "google.apps.sheets.v4.SpreadsheetsService.GetSpreadsheet"
}
}
]
}
}
クライアントライブラリでやるには?
code:index.js
pu
普通に Workload Identity が有効な gke 上でこれ実行したらシート読めた
google-auth-library がよしなに metadata server 叩いているんだろう
ローカルで gcloud auth login & gcloud auth application-default loginしていても通らないなあ
Gaxios のエラーメッセージの Token で tokeinfo を叩く
curl -H 'Authorization: Bearer {TOKEN}' https://oauth2.googleapis.com/tokeinfo
code:tokeinfo
...
"scope": "openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/accounts.reauth",
spreadsheet がない
コードリーディングの結果、WellKnownFile が OAuth2 Client Secret の場合は GoogleAuth に渡した scope が設定されない(まあそうか)
Client Secret を生成する際に scope を設定するといける
gcloud auth application-default login - gcloud auth application-default login  |  Cloud SDK のドキュメント のオプションに --scopes がある
$ gcloud auth application-default login --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/spreadsheets.readonly
デフォルトの userinfo.email や accounts.reauth がないと expire して困る?
ttl 程度待ってみる
1日経っても叩ける
これで生成した application_default_credentials.json が置かれていたらおもむろに spreadsheet api を叩ける!!
Cloud Functions でも確認
取れた
Compute Engine のインスタンスに scope を設定していたら metadata server がリクエスト時の scope パラメータを無視するという話
あるらしいが手元で確認はしてない
---.icon
const sheets = google.sheets({version: 'v4', auth: authClient }); ← これ追う
googleapis/nodejs-googleapis-common: A set of common APIs and tools for Google npm modules.
googleapis/nodejs-googleapis-common@master - src/authplus.ts
google.auth の実体
googleapis/google-api-nodejs-client@master - src/apis/sheets/index.ts#L24
google.sheets(...)
引数は sheets_v4.Options
googleapis/google-api-nodejs-client@master - src/apis/sheets/v4.ts#L40L42
GlobalOptions
googleapis-common のこれ、auth のみ
googleapis/nodejs-googleapis-common@98edc86 - src/api.ts#L46
auth?: GoogleAuth | OAuth2Client | BaseExternalAccountClient | string;
返り値は Sheets
getAPI<T = sheets.v4.Sheets> googleapis/nodejs-googleapis-common@08a0118 - src/apiIndex.ts#L16
Sheets googleapis/google-api-nodejs-client@master - src/apis/sheets/v4.ts#L114
sheets.spreadsheets.values
spreadsheets は Resource$Spreadsheets
googleapis/google-api-nodejs-client@master - src/apis/sheets/v4.ts#L4929
spreadsheets.values も同様に Resource$Spreadsheets$Values
googleapis/google-api-nodejs-client@master - src/apis/sheets/v4.ts#L6085
リソースの階層は $ 区切り
context: APIRequestContext を受け渡す
googleapis/nodejs-googleapis-common@master - src/api.ts#L37
GlobalOptions を持ってる、google も GlobalOptions
get(...)
googleapis/google-api-nodejs-client@06cd1d9 - src/apis/sheets/v4.ts#L7382
parameters の context
createAPIRequest を呼ぶ
createAPIReqeust
googleapis/nodejs-googleapis-common@master - src/apirequest.ts#L73
parameters: APIRequestParams に一通りの値が入って呼ばれる
例
options.url, options.method, API のリクエスト先情報
params.spreadsheetId, params.range, API のパラメータ
requiredParams, pathParams, パラメータバリデーションに使う?
context
params.auth, options.auth の順に authClient として束縛
googleapis/nodejs-googleapis-common@master - src/apirequest.ts#L132
authClient が文字列なら API Key として使う(params.key にセットする)
googleapis/nodejs-googleapis-common@master - src/apirequest.ts#L319
options.http2 でなければ OAuth2Client を仮定してリクエスト
authClient.request(options)
authClient.request
中で getClient している
googleapis/google-auth-library-nodejs@master - src/auth/googleauth.ts#L855
つまり auth(new google.auth.GoogleAuth したインスタンス)を渡しても、auth.getClient() した結果どちらを渡しても良い?
ダメそうに思えるけどどちらも request メソッドが共通
GoogleAuth を渡していたら client を呼んで request する
auth : GoogleAuth#request → GoogleAuth#getClient → client.request
authClient: client.request
getClient
googleapis/google-auth-library-nodejs@master - src/auth/googleauth.ts#L778
GKE 上だと Compute
ローカルでなんも指定しないと UserRefreshClient
cachedCredentials > keyFilename(サービスアカウントキー) > getApplicationDefaultAsync
getApplicationDefaultAsync
googleapis/google-auth-library-nodejs@master - src/auth/googleauth.ts#L267
EnvironmentVariable > WellKnownFile > Compute
Compute 以外は Client が JWT, BaseExternalAccountClient の場合に scope が設定される
ローカルでなんも指定しない OAuth2 Client Secret の場合(UserRefreshClient)は設定されない
BaseExternalAccountClient
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/baseexternalclient.ts#L118
External = AWS, GCE, OIDC-based providers
仮にコードを書き換えて scope 設定したらどうなる?
RefreshClient に scope 設定する余地ない
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/refreshclient.ts
getClient したものって結局何
credentials の request メソッドはどこからくる
UserRefreshClient は OAuth2Client
googleapis/google-auth-library-nodejs@master - src/auth/refreshclient.ts
JWTClient は OAuth2Client, IdTokenProvider
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/jwtclient.ts#L38
BaseExternalAccountClient は自前で実装
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/baseexternalclient.ts#L273
fromStreamAsync, stream でファイル読む参考実装として
googleapis/google-auth-library-nodejs@master - src/auth/jwtclient.ts#L326
OAuth2Client#request
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/oauth2client.ts#L915
getRequestMetadataAsync
UserRefreshClient は OAuth2Client の実装を使う
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/oauth2client.ts#L779
JWTClient は override していてここで JWT からトークン取ってくるのだろう
googleapis/google-auth-library-nodejs@e5ab8e5 - src/auth/jwtclient.ts#L122
リクエスト投げるところでどう Authorization ヘッダを作っている?
impersonate はどこで考慮されている?
cachedClient にImpersonated 型がある
CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT → 使えない
名前的にも CLOUDSDK 用か
オプションや環境変数からいい感じにディスカバリしてくれる仕組みはなくて、自分で Impersonated なクライアントインスタンスを作って渡す
Impersonated Credentials Client googleapis/google-auth-library-nodejs: 🔑 Google Auth Library for Node.js
const {Impersonated} = require('google-auth-library'); を使って認証する
クライアントライブラリの auth に { getClient: () => Impersonated } なオブジェクトを渡す
まだ限定的な対応、Spreadsheet で使うには token 取ってきて明に渡せば良い?
---.icon
googleapis/google-auth-library-nodejs: 🔑 Google Auth Library for Node.js
googleapis/google-api-nodejs-client: Google's officially supported Node.js client library for accessing Google APIs. Support for authorization and authentication with OAuth 2.0, API Keys and JWT (Service Tokens) is included.
GCP の Compute Metadata Credentials について https://zenn.dev/apstndb/articles/10349708f25985
---.icon
~/.config/gcloud/application_default_credentials.json は OAuth2 Client Secret
GOOGLE_APPLICATION_CREDENTIALS とは違う
google cloud platform - How to get application_default_credentials using service account? - Stack Overflow
code:spreadsheet.js